package mantel;

import java.util.ArrayList;

import jsc.combinatorics.Permutation;
import jsc.combinatorics.Permutations;
import jsc.datastructures.PairedData;

import tool.Utility;

public class DistanceMatrix {
	private double[][] d;
	private int size;
	private int n;
	
	public static void main(String args[]){
		
		DistanceMatrix dm = new DistanceMatrix(5);
		System.out.println(dm);
		dm.set(4,1.0); dm.set(5,2.0);
		System.out.println(dm);

		
		double[] v = dm.unfold();
		for (int i = 0; i < v.length; i++) {
			System.out.println(v[i]);
		}
	}
	
	public DistanceMatrix(int size){
		this.size = size;
		d = new double[size-1][];
		for (int i = 0; i < size-1; i++) {
			d[i] = new double[i+1];
		}
		n = (size*size-size)/2;
	}
	
	public String toString(){
		String s = "";
		for(int i=0;i<size-1;i++){
			for(int j=0;j<i;j++){
				s = s+d[i][j]+" ";
			}
			s = s+d[i][i]+"\n";
		}
		return s;
	}
	
	public double get(int x){
		int p = (int)triRoot(x);
		int pT = (p*(p+1)/2);
		int q = x-pT;
		return get(p,q);
	}
	
	public static double triRoot(int x){
		return (-1+Math.sqrt(8*x+1))/2;
	}
	
	public void set(int x, double r){
		int p = (int)triRoot(x);
		int pT = (p*(p+1)/2);
		int q = x-pT;
		set(p,q,r);
		
	}
	
	public double getInFullMatrix(int x, int y){
		if(x==y){
			return 0.0;
		}
		else {
			if(x<y){
				return get(y,x);
			}
			else {
				return get(x,y);
			}
		}
	}
	
	public double get(int x, int y){
		try{
			return d[x][y];
		}
		catch(ArrayIndexOutOfBoundsException e){
			System.out.println("Failed to get index entry: check indices "+x+" "+y);
			e.printStackTrace();
		}
		return 0.0;
	}
	
	public void set(int x, int y, double r){
		try{
			d[x][y] = r;
		}
		catch(ArrayIndexOutOfBoundsException e){
			System.out.println("Failed to get index entry: check indices "+x+" "+y);
			e.printStackTrace();
		}
	}
	
	public double[][] toArray(){
		double[][] dFull = new double[size][size];
		for(int i=0;i<size-1;i++){
			for(int j=0;j<=i;j++){
				dFull[i+1][j]=d[i][j];
				dFull[j][i+1]=d[i][j];
			}
		}
		return dFull;
	}
	
	public double[] unfold(){
		int n = size*size;
		double[] v = new double[(n-size)/2];
		int z = 0;
		for(int i=0;i<size-1;i++){
			for(int j=0;j<=i;j++){
				v[z] = d[i][j];
				z = z+1;
			}
		}
		return v;
	}
	
	public ArrayList<Double> toReducedVector(double min, double max){
		
		ArrayList<Double> rv = new ArrayList<Double>();
		
		for(int i=0;i<size-1;i++){
			for(int j=0;j<=i;j++){
				if(d[i][j]>= min && d[i][j]<max){
					rv.add(d[i][j]);
				}
			}
		}
		return rv;
	}
	
	public PairedData getPairedData(DistanceMatrix x, double[] bounds){
		ArrayList<Double> temp1 = new ArrayList<Double>();
		ArrayList<Double> temp2 = new ArrayList<Double>();
		
		double min = bounds[0]; double max = bounds[1];
		
		for(int i=0;i<size-1;i++){
			for(int j=0;j<=i;j++){
				if(d[i][j]>= min && d[i][j]<max){
					temp1.add(d[i][j]);
					temp2.add(x.get(i, j));
				}
			}
		}
		double[] t1 = new double[temp1.size()];
		double[] t2 = new double[temp2.size()];
		int s = 0;
		
		for(Double d : temp1){
			t1[s] = d;
			s = s+1;
		}
		
		s = 0;
		for(Double d : temp2){
			t2[s] = d;
			s = s+1;
		}
		
		return new PairedData(t1,t2);
	}
	
	public DistanceMatrix permute(Permutations permutations){
		Permutation p = permutations.randomPermutation();
		int[] k = p.toIntArray();
		int[] k0 = new int[k.length];
		for(int i=0;i<k.length;i++){k0[i] = k[i]-1;} //convert the permutation to 0 indexed
		
		DistanceMatrix M = new DistanceMatrix(this.size);
		for (int i = 0; i < k0.length; i++) {
			int a = k0[i];
			for (int j = 0; j <= i; j++) {
				int b = k0[j];
				if(b>a){int tmp = b; b = a; a=tmp;}
				M.getD()[i][j]=d[a][b];
				a = k0[i]; //reswap back for next elements!! Fixes bug
			}
		}
		return M;
	}

	public double[][] getD() {
		return d;
	}

	public void setD(double[][] d) {
		this.d = d;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public int getN() {
		return n;
	}

	public void setN(int n) {
		this.n = n;
	}
	
	
}
